#include <jni.h>
#include <string>
#include "Calculator.h"
#include <android/log.h>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <linux/elf.h>
#include <linux/mman.h>
#include <sys/mman.h>
#include "PLTHook.h"

#define LOG_TAG "JNI_DEMO"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)

#ifndef DT_GNU_HASH
#define DT_GNU_HASH 0x000000006ffffef5
#endif

extern "C" JNIEXPORT jstring JNICALL
stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}
extern "C"
JNIEXPORT jint JNICALL
sum(JNIEnv *env, jobject thiz, jint a, jint b) {
    LOGD("sum native method is called");
    return Calculator::getCalc()->sum(a, b);
}
extern "C"
JNIEXPORT jint JNICALL
minus(JNIEnv *env, jobject thiz, jint a, jint b) {
    LOGD("minus native method is called");
    return Calculator::getCalc()->minus(a, b);
}

void exit() {
    LOGD("Application is Exiting ......");
}


extern "C"
JNIEXPORT void JNICALL
killProcess(JNIEnv *env, jobject thiz) {
    exit(0);
}


extern "C"
JNIEXPORT void JNICALL
registerExitFun(JNIEnv *env, jobject thiz) {
    LOGD("register exit function");
    atexit(exit);
}

extern "C"
JNIEXPORT jint JNICALL
accumulate(JNIEnv *env, jobject thiz, jintArray elements) {
    int *pNum = (int *) malloc(sizeof(int));
    *pNum = 8;
    LOGD("pNum, %d", *pNum);
    free(pNum);

    jboolean isCopy = JNI_FALSE;
    jint *pElement = env->GetIntArrayElements(elements, &isCopy);
//    jint* pElement = (jint*) env->GetPrimitiveArrayCritical(elements, &isCopy);  // 较GetIntArrayElements()，会增大返回直接指针的概率
    jsize len = env->GetArrayLength(elements);
    int acc = 0;
    int offset;
    for (offset = 0; offset < len; offset++) {
        acc += *(pElement + offset);
        if (offset == 3) {
            *(pElement + offset) = 100;
        }
    }
    env->ReleaseIntArrayElements(elements, pElement, 0);
//    env->ReleasePrimitiveArrayCritical(elements, pElement, JNI_ABORT);
    LOGD("isCopy=%d, offset=%d, acc=%d", isCopy, offset, acc);
    return acc;
}


uintptr_t  originFunc2 = 0;
void *myMalloc2(size_t byteCount) {
    LOGD("myMalloc2(), malloc function is called ......");
    return reinterpret_cast<void*(*)(size_t)>(originFunc2)(byteCount);  // void*(*)(size_t) 函数指针类型
}

extern "C"
JNIEXPORT void JNICALL
pltHook(JNIEnv *env, jobject thiz) {

    PLTHook pltHook;
    uintptr_t soBaseAddress = pltHook.parseSOBaseAddress("libcppdemo.so");
    pltHook.setNewFuncPtr(reinterpret_cast<uintptr_t>(myMalloc2));
    std::pair<uintptr_t, uint64_t> dynSegAddress = pltHook.getDynAddress(soBaseAddress);
    originFunc2 =pltHook.parseRELPLT(soBaseAddress, dynSegAddress);
}


jint JNI_OnLoad(JavaVM *vm, void *reserved) {
    JNIEnv *env;
    if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
        return JNI_ERR;  // JNI version not supported.
    }

    // 注册本地方法
    const char *className = "com/ai/cppdemo/MainActivity";
    jclass classToRegister = env->FindClass(className);
    if (classToRegister == nullptr) {
        return JNI_ERR;  // Class not found.
    }

    // 注册方法（如果有的话）
    static JNINativeMethod methods[] = {
            {"stringFromJNI",   "()Ljava/lang/String;", (void *) stringFromJNI},
            {"sum",             "(II)I",                (void *) sum},
            {"minus",           "(II)I",                (void *) minus},
            {"registerExitFun", "()V",                  (void *) registerExitFun},
            {"killProcess", "()V",                  (void *) killProcess},
            {"accumulate",      "([I)I",                (void *) accumulate},
            {"pltHook",         "()V", (void *) pltHook},
    };
    env->RegisterNatives(classToRegister, methods, sizeof(methods) / sizeof(methods[0]));

    return JNI_VERSION_1_6;  // Return the JNI version supported.
}